module factories.util;

public import factories.container;

import factories : modules;

/// 地图种子
uint seed;

/// 生成随机数
auto rand() => (seed = xorshift32(seed ^ 0xdeadbeef)) / double(uint.max);

uint xorshift32(uint x) {
	x ^= x << 13;
	x ^= x >> 17;
	x ^= x << 5;
	return x;
}

unittest {
	float sum = 0;
	enum N = 1_000_000;
	foreach (i; 0 .. N) {
		const r = rand();
		assert(r >= 0 && r <= 1);
		sum += r;
	}
	assert(sum > .49 * N && sum < .51 * N);
}

/// 32 字节对齐
auto align32(size_t x) => (x + 31) & ~31;

unittest {
	assert(align32(0) == 0);
	assert(align32(1) == 32);
	assert(align32(32) == 32);
	assert(align32(33) == 64);
	assert(align32(64) == 64);
	assert(align32(65) == 96);
}

version (unittest) {
	import core.stdc.math : sin, floor;
} else {
	extern (C) {
		float sin(float x);
		float floor(float x);
	}
}

float saturate(float x) =>
	x < 0 ? 0 : x > 1 ? 1 : x;

float rand(float x, float y) {
	const f = sin(x * 12.9898 + y * 78.233) * 43_758.5453;
	return f - floor(f);
}

unittest {
	float sum = 0;
	enum N = 1_000;
	foreach (i; 0 .. N) {
		foreach (j; 0 .. N) {
			const r = rand(i, j);
			assert(r >= 0 && r <= 1);
			sum += r;
		}
	}
	assert(sum > .49 * N * N && sum < .51 * N * N);
}

float mix(float a, float b, float t) =>
	a + (b - a) * t;

/// 噪声
float noise(float x, float y) {
	import core.simd;

	const xi = cast(int)x;
	const yi = cast(int)y;
	float2 f = void;
	f[0] = x - xi;
	f[1] = y - yi;

	float a = rand(xi, yi);
	float b = rand(xi + 1, yi);
	float c = rand(xi, yi + 1);
	float d = rand(xi + 1, yi + 1);

	const float2 u = f * f * (3 - 2 * f);

	return mix(a, b, u[0])
		+ (c - a) * u[1] * (1 - u[0])
		+ (d - b) * u[0] * u[1];
}

/// 多重噪声
float octavedNoise(float x, float y, float frequency, float amplitude,
	uint octaves = 5, float freqStep = 2, float ampStep = .5) {
	float sum = 0,
	amp = 0;
	for (uint i; i < octaves; i++) {
		sum += noise(x * frequency, y * frequency) * amplitude;
		amp += amplitude;
		frequency *= freqStep;
		amplitude *= ampStep;
	}
	return sum / amp;
}

struct persist { // @suppress(dscanner.style.phobos_naming_convention)
	int order;
}

/// 获取需要持久化的数据
template getPersists(T...) {
	import std.meta;
	import std.traits;

	alias getPersists = AliasSeq!();
	static foreach (f; T)
		getPersists = AliasSeq!(getPersists, getSymbolsByUDA!(f, persist));
	enum cmp(alias a, alias b) =
		getUDAs!(a, persist)[0].order < getUDAs!(b, persist)[0].order;
	getPersists = staticSort!(cmp, getPersists);
}

/// 写入数据
extern (C) void write(void* data, size_t size);

alias Data = getPersists!modules;

/// 保存游戏
extern (C) export void save() {
	foreach (i, T; Data) {
		write(&T, T.sizeof);
	}
}
